package com.guoj.rabbitmq.receive;

import com.guoj.rabbitmq.entity.TOrder;
import com.guoj.rabbitmq.utils.Constant;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.*;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Headers;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.stereotype.Component;

import java.util.Map;

/**
 * @作者: guoj
 * @日期: 2019/5/31 09:39
 * @描述: rabbitmq-消费方
 */
@Component
@Slf4j
public class OrderReceive {

	/**
	 * 使用这个注解，可以自动在rabbitmq中创建出交换机、队列及routingKey的绑定关系。
	 * 使用时，可以先启动消费方把这些关系都自动创建出来。
	 *
	 * exchange 交换机
	 * ---------------------
	 * name：交换机名称
	 * durable: 是否持久化
	 * type: 消息模式（direct、topic、fanout、header）
	 * ignoreDeclarationExceptions: 忽略声明异常
	 *
	 * value 队列
	 * --------------------
	 * value: 哪个队列
	 * durable：是否持久化
	 *
	 * key 路由key
	 */
	@RabbitHandler
	@RabbitListener(bindings = @QueueBinding(
			exchange = @Exchange(
				name = Constant.ORDER_EXCHANGE,
				durable = Constant.DURABLE,
				type = Constant.MESSAGE_TYPE,
				ignoreDeclarationExceptions = Constant.IGNORE_DECLARATION_EXCEPTIONS
			),
			value = @Queue(
				value = Constant.ORDER_QUEUE,
				durable = Constant.DURABLE
			),
			key = Constant.ORDER_CONSUMER_KEY
	))
	public void onOrderMessage(@Payload TOrder order, Channel channel,
			@Headers Map<String, Object> headers)
			throws Exception {

		// 消费消息
		log.info("-----------------------收到消息, 开始消费-----------------------");
		log.info("订单id: {}", order.getId());

		// 代表了 RabbitMQ 向该 Channel 投递的这条消息的唯一标识 ID，是一个单调递增的正整数，delivery tag 的范围仅限于 Channel
		Long deliveryTag = (Long) headers.get(AmqpHeaders.DELIVERY_TAG);

		/**
		 * 手工ACK，实际中一般使用手工签收，自动签收容易丢失消息。
		 * 这行如果注释掉，也能收到消息，但是rabbitmq的消息还在，没有没签收。
		 * 第二个参数是multiple：为了减少网络流量，手动确认可以被批处理，当该参数为 true 时，则可以一次性确认 delivery_tag 小于等于传入值的所有消息
		 */
		channel.basicAck(deliveryTag, false);
	}
}
